﻿using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Moq;
using NUnit.Framework;
using SessionHunter.DataAccess;
using SessionHunter.Model;

namespace SessionHunter.UnitTests.DataAccess
{
    /// <remarks>MOQ is required here, because we can't access isolated storage from unit tests</remarks>
    [TestFixture]
    public class TestApplicationRepository : TestBase
    {
        /// <summary>
        /// An example session setting from file
        /// </summary>
        private const string _sessionSettingsXML = @"<?xml version=""1.0"" encoding=""utf-8""?><Settings><ShowCreateExpires>False</ShowCreateExpires><ShowLockInfo>False</ShowLockInfo></Settings>";

        /// <summary>
        /// An example session setting from file with references to load
        /// </summary>
        private const string _sessionSettingsXMLWithReferences = @"<?xml version=""1.0"" encoding=""utf-8""?><Settings><ShowCreateExpires>False</ShowCreateExpires><ShowLockInfo>False</ShowLockInfo><References><Reference>C:\Projects\Session\Code\TestDataGenerator\bin\TestDataGenerator.dll</Reference><Reference>C:\Projects\Session\Code\TestDataGenerator\bin\TestDataGenerator2.dll</Reference></References></Settings>";

        /// <summary>
        /// Simulates the XDocument we store on file
        /// </summary>
        private XDocument _sessionXML;

        [TestFixtureSetUp]
        public void TestSetup()
        {
            // Always clear out the data we "save" during each test
            _sessionXML = null;
        }

        /// <summary>
        /// Tests setting and then getting data out of out settings store
        /// </summary>
        [Test]
        public void SaveOption_TestSaveOption_GetReturnsValidData()
        {
            // Assemble
            // Get some XML we will "load" from disk
            _sessionXML = XDocument.Parse(_sessionSettingsXML);

            // Mock out the method, since we can't access isolated storage from unit tests
            var repo = new Mock<ApplicationRepository>() { CallBase = true };
            repo.Setup(mock => mock.GetSettingsFromStorage(It.IsAny<string>())).Returns(_sessionXML);
            // Accept any XDocument that is passed in, but don't do anything with it
            repo.Setup(mock => mock.SaveSettingsToStorage(It.IsAny<XDocument>()));

            // Save an int
            repo.Object.SaveOption<int>(ApplicationRepository.PersistedSettings.NumberOfSessions, 99);
            // Save an enum
            repo.Object.SaveOption<System.Enum>(ApplicationRepository.PersistedSettings.OrderBy, ApplicationOptions.OrderByParameter.SessionId);
            // Save a bool
            repo.Object.SaveOption<bool>(ApplicationRepository.PersistedSettings.ShowCreateExpires, true);

            // Act
            // Now validate that we got the same values back
            var options = repo.Object.GetOptions();

            // Assert
            Assert.AreEqual(options.NumberOfSessions, 99, "NumberOfSessions does not match the value we set it to");
            Assert.AreEqual(options.OrderBy, ApplicationOptions.OrderByParameter.SessionId, "OrderByParameter does not match the value we set it to");
            Assert.AreEqual(options.ShowCreateExpires, true, "ShowCreateExpires does not match the value we set it to");
        }

        /// <summary>
        /// Tests saving all of the references
        /// </summary>
        [Test]
        public void SaveReferences_SaveTwoReferences_DataIsSaved()
        {
            // Assemble
            // Get some XML we will "load" from disk
            _sessionXML = XDocument.Parse(_sessionSettingsXML);

            // Mock out the method, since we can't access isolated storage from unit tests
            var repo = new Mock<ApplicationRepository>() { CallBase = true };
            repo.Setup(mock => mock.GetSettingsFromStorage(It.IsAny<string>())).Returns(_sessionXML);
            // Accept any XDocument that is passed in, but don't do anything with it
            repo.Setup(mock => mock.SaveSettingsToStorage(It.IsAny<XDocument>())).Callback((XDocument xdoc) =>
            {
                var list = from reference in xdoc.Descendants("Reference")
                           select reference.Value;

                // Assert
                Assert.IsTrue(list.Contains("C:\\Projects\\Session\\Code\\TestDataGenerator\\bin\\TestDataGenerator.dll"), "Could not find the reference we added");
                Assert.IsTrue(list.Contains("C:\\Projects\\Session\\Code\\TestDataGenerator\\bin\\TestDataGenerator2.dll"), "Could not find the reference we added");
            });

            var references = new List<ExternalReference>();
            references.Add(new ExternalReference() { Path = "C:\\Projects\\Session\\Code\\TestDataGenerator\\bin\\TestDataGenerator.dll" });
            references.Add(new ExternalReference() { Path = "C:\\Projects\\Session\\Code\\TestDataGenerator\\bin\\TestDataGenerator2.dll" });

            // Act
            // Add two references
            repo.Object.SaveReferences(references);
        }

        /// <summary>
        /// Tests saving all of the references when there hasn't been anything saved yet
        /// </summary>
        [Test]
        public void SaveReferences_SaveReferenceIntoEmptyFile_DataIsSaved()
        {
            // Assemble

            // Mock out the method, since we can't access isolated storage from unit tests
            var repo = new Mock<ApplicationRepository>() { CallBase = true };
            repo.Setup(mock => mock.GetSettingsFromStorage(It.IsAny<string>())); // Return null
            // Accept any XDocument that is passed in, but don't do anything with it
            repo.Setup(mock => mock.SaveSettingsToStorage(It.IsAny<XDocument>())).Callback((XDocument xdoc) =>
            {
                // Assert
                var list = from reference in xdoc.Descendants("Reference")
                           select reference.Value;

                Assert.IsTrue(list.Contains("C:\\Projects\\Session\\Code\\TestDataGenerator\\bin\\TestDataGenerator.dll"), "Could not find the reference we added");
                Assert.IsTrue(list.Contains("C:\\Projects\\Session\\Code\\TestDataGenerator\\bin\\TestDataGenerator2.dll"), "Could not find the reference we added");
            });

            var references = new List<ExternalReference>();
            references.Add(new ExternalReference() { Path = "C:\\Projects\\Session\\Code\\TestDataGenerator\\bin\\TestDataGenerator.dll" });
            references.Add(new ExternalReference() { Path = "C:\\Projects\\Session\\Code\\TestDataGenerator\\bin\\TestDataGenerator2.dll" });


            // Act
            // Add two references
            repo.Object.SaveReferences(references);
        }

        /// <summary>
        /// Checks we populate the reference data from the XML properly
        /// </summary>
        [Test]
        public void GetOptions_HasReferenceXML_ReturnsList()
        {
            // Assemble
            // Get some XML we will "load" from disk
            _sessionXML = XDocument.Parse(_sessionSettingsXMLWithReferences);

            // Mock out the method, since we can't access isolated storage from unit tests
            var repo = new Mock<ApplicationRepository>() { CallBase = true };
            repo.Setup(mock => mock.GetSettingsFromStorage(It.IsAny<string>())).Returns(_sessionXML);

            // Act
            // Now validate that we got the same values back
            var options = repo.Object.GetOptions();

            // Assert
            Assert.AreEqual(2, options.ReferencePaths.Count);
        }
    }
}
